package com.dongbao.core.rules.java;

import com.dongbao.core.base.CodeRulesCommonBase;
import com.dongbao.core.rules.CodeRulesBase;
import com.dongbao.entity.AwardedInfo;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiClassImpl;
import com.intellij.psi.impl.source.tree.PsiCommentImpl;
import com.intellij.psi.impl.source.tree.java.PsiExpressionStatementImpl;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.tree.IElementType;
import org.jsoup.internal.StringUtil;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 检测方法的注解是否合格,对应代码检查规范中【代码结构】-【方法结构要体现主逻辑清晰（列点，体现核心思路）】
 * */
public class AnnotationsCodeRules extends CodeRulesCommonBase implements CodeRulesBase {

    private List<String> mustAnno = Arrays.asList("param","return","author","date");

    public AnnotationsCodeRules(VirtualFile afterFile,PsiFile psiFile,Project project) {
        super(afterFile,psiFile,project);
        this.code = "A001";
        this.level = "A";
    }

    /**
     * 执行规则判断
     * */
    @Override
    public void run(List<AwardedInfo> deduction) {
        /* 执行分析当前class是否负责当前规则 */
        PsiAnnotation[] annotations = psiClassImpls.get(0).getModifierList().getAnnotations();
        for(PsiClassImpl psiClass:psiClassImpls){
            /* 获取所有方法 */
//            PsiMethod[] allMethods = psiClass.getMethods();
            /* todo:只计算当前真实存在的方法 */
            List<PsiMethod> ownMethods = psiClass.getOwnMethods();
            /* 读取方法上的注释检查所有方法的注释是否符合要求 */
            for(PsiMethod method:ownMethods){
                /* 判断当前方法注释中是否存在固定的几个标签 @param  @return @author @date */
                valiAnno(deduction,psiClass,method);
                /* 验证当前方法中是否存在对于流程的注释 */
                valiProcess(deduction,psiClass,method);
            }
        }
    }

    /**
     * 验证方法的注释上是否存在 固定的几个标签 @param  @return @author @date
     * */
    public Boolean valiAnno(List<AwardedInfo> deduction,PsiClassImpl psiClass,PsiMethod method){
        if(method.getDocComment()==null){
            return true;
        }
        PsiDocTag[] tags = method.getDocComment().getTags();
        List<String> notExistTag = mustAnno.stream().filter(item -> {
            return Arrays.stream(tags).filter(tag->tag.getName().equals(item)).collect(Collectors.toList()).size()==0;
        }).collect(Collectors.toList());
        if(notExistTag.size()>0){
            /* 获取错误代码行位置 */
            Document document = PsiDocumentManager.getInstance(psiClass.getProject()).getDocument(psiClass.getContainingFile());
            int lineNumber = document.getLineNumber(method.getTextOffset());

            /* 方法上的注解缺少指定tag标签 */
            packDeduction(
                    deduction,
                    psiClass.getName(),
                    psiPackageStatements.get(0).getPackageName(),
                    method.getName(),
                    String.format("Java类[%s]的方法[%s]上注解缺少[%s]标签说明",psiClass.getName(),method.getName(),notExistTag.stream().collect(Collectors.joining(","))),
                    lineNumber,lineNumber);
            return false;
        }
        return true;
    }

    /**
     * 当方法行数超过20行时进行验证
     * 验证当前方法流程是否符合规范,代码行数超过十行就进行验证,判断是否存在带数字的注释
     * 例如:\/* 1.*\/
     * */
    public Boolean valiProcess(List<AwardedInfo> deduction,PsiClassImpl psiClass,PsiMethod method){
        List<PsiElement> childrens = Arrays.asList(method.getBody().getChildren());
        /* 获取当前方法的行数 */
        if(method.getBody().getChildren().length>20 && childrens!=null && !childrens.isEmpty()){
            /* 判断当前方法中是否存在注释 */
            List<PsiCommentImpl> allComment=new ArrayList<PsiCommentImpl>();
            for(PsiElement children:childrens){
                if(children instanceof PsiCommentImpl){
                    allComment.add((PsiCommentImpl)children);
                }
            }
            /* 如果没有找到注释的内容则直接进行提示没有此方法没有注释 */
            if(allComment.isEmpty()){
                Document document = PsiDocumentManager.getInstance(psiClass.getProject()).getDocument(psiClass.getContainingFile());
                int lineNumber = document.getLineNumber(method.getTextOffset());

                packDeduction(
                        deduction,
                        psiClass.getName(),
                        psiPackageStatements.get(0).getPackageName(),
                        method.getName(),
                        String.format("Java类[%s]的方法[%s]中未存在注释信息",psiClass.getName(),method.getName()),
                        lineNumber,lineNumber);
                return false;
            }
            /* 验证注释中是否存在编号数字的注释内容 */
            Boolean allCommentResult = false;
            List<String> nums=Arrays.asList("1","2","3","4","5","6","7","8","9");
            for(PsiCommentImpl comment:allComment){
                IElementType tokenType = comment.getTokenType();
                /* C_STYLE_COMMENT:多行注释  END_OF_LINE_COMMENT:单行注释 */
                /* 读取注释中的内容 */
                String content = comment.getText().substring(2, comment.getText().length());
                /* 判断是否存在数字开头第一位 */
                if(StringUtil.isBlank(content)){
                    continue;
                }
                Boolean contains = nums.contains(content.trim().substring(0, 1));
                if(contains){
                    allCommentResult = true;
                }
            }
            if(!allCommentResult){
                /* 获取错误代码行位置 */
                Document document = PsiDocumentManager.getInstance(psiClass.getProject()).getDocument(psiClass.getContainingFile());
                int lineNumber = document.getLineNumber(method.getTextOffset());

                packDeduction(
                        deduction,
                        psiClass.getName(),
                        psiPackageStatements.get(0).getPackageName(),
                        method.getName(),
                        "方法未使用数字来表名流程,请使用数字作为注释的开头来说明流程",lineNumber,lineNumber);
                return false;
            }
        }
        return true;
    }

    @Override
    public String describe() {
        return "代码结构";
    }
}
